package com.sina.medical.core.util;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.NumberSerializer;
import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.sina.medical.core.Constant;
import org.hibernate.SessionFactory;

import java.io.IOException;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.TimeZone;

import static java.math.RoundingMode.UNNECESSARY;

/**
 * created  on 19/07/2017.
 */
public abstract class JsonUtils {


    /**
     * 公用的 ObjectMapper 请不要在运行中修改 配置,若要修改配置请使用
     * {@link #createDefaultObjectMapper(SessionFactory)} 创建新的对象
     */
    public static final ObjectMapper OBJECT_MAPPER;

    public static final LocalDateTimeDeserializer LOCALDATETIME_DESERIALIZER = new LocalDateTimeDeserializer( Constant.DATETIME_FORMATTER );

    public static final LocalDateTimeSerializer LOCALDATETIME_SERIALIZER = new LocalDateTimeSerializer( Constant.DATETIME_FORMATTER );

    public static final LocalDateDeserializer LOCALDATE_DESERIALIZER = new LocalDateDeserializer( Constant.DATE_FORMATTER );

    public static final LocalDateSerializer LOCALDATE_SERIALIZER = new LocalDateSerializer( Constant.DATE_FORMATTER );

    public static final NumberSerializer BIG_DECIMAL_NUMBER_SERIALIZER = new NumberSerializer( BigDecimal.class ) {
        @Override
        public void serialize(Number value, JsonGenerator g, SerializerProvider provider) throws IOException {
            BigDecimal b = (BigDecimal) value;
            if(b.scale() < 1){
                // 当没有 小数时,带上两位小数
                b = b.setScale( 2 ,UNNECESSARY);
            }
            g.writeString( b.toPlainString() );
        }
    };

    public static final NumberSerializer BIG_INTEGER_NUMBER_SERIALIZER = new NumberSerializer( BigInteger.class ) {
        @Override
        public void serialize(Number value, JsonGenerator g, SerializerProvider provider) throws IOException {
            g.writeString( value.toString() );
        }
    };

    public static final NumberSerializer LONG_NUMBER_SERIALIZER = new NumberSerializer( Long.class ) {
        @Override
        public void serialize(Number value, JsonGenerator g, SerializerProvider provider) throws IOException {
            g.writeString( value.toString() );
        }
    };

    /**
     * long 原型
     */
    public static final NumberSerializer LONG_TYPE_NUMBER_SERIALIZER = new NumberSerializer( Long.TYPE ) {
        @Override
        public void serialize(Number value, JsonGenerator g, SerializerProvider provider) throws IOException {
            g.writeString( value.toString() );
        }
    };


    static {
        OBJECT_MAPPER = createDefaultObjectMapper();
    }

    private JsonUtils() {

    }

    /**
     * 添加 java 8 时间特性({@link LocalDateTime}),并指定时区为东八区
     */
    public static ObjectMapper createDefaultObjectMapper(SessionFactory sessionFactory) {

        ObjectMapper mapper = new ObjectMapper();

        mapper.registerModule( createJavaTimeModule() );

        mapper.registerModule( createSinaCustomModule( null ) );

        mapper.registerModule( createHibernate5Module( sessionFactory ) );

        SimpleDateFormat format = new SimpleDateFormat( Constant.dateTimeFormat );
        format.setTimeZone( TimeZone.getTimeZone( Constant.timeZone8 ) );
        mapper.setDateFormat( format );

        DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
        prettyPrinter.indentObjectsWith( new DefaultIndenter( "  ", "\n" ) );
        mapper.setDefaultPrettyPrinter( prettyPrinter );
        return mapper;
    }


    public static ObjectMapper createDefaultObjectMapper() {
        return createDefaultObjectMapper( null );
    }

    private static Module createSinaCustomModule(String moduleName) {
        // 自定义模块,重写部分序列化器
        return new SimpleModule( moduleName == null ? "sinaFinanceCustomModule" : moduleName )
                .addSerializer( BigDecimal.class, BIG_DECIMAL_NUMBER_SERIALIZER )
                .addSerializer( BigInteger.class, BIG_INTEGER_NUMBER_SERIALIZER )
                .addSerializer( Long.class, LONG_NUMBER_SERIALIZER )
                .addSerializer( Long.TYPE, LONG_TYPE_NUMBER_SERIALIZER )
                ;
    }

    private static Module createJavaTimeModule() {
        return new JavaTimeModule()
                .addDeserializer( LocalDateTime.class, LOCALDATETIME_DESERIALIZER )
                .addSerializer( LocalDateTime.class, LOCALDATETIME_SERIALIZER )

                .addDeserializer( LocalDate.class, LOCALDATE_DESERIALIZER )
                .addSerializer( LocalDate.class, LOCALDATE_SERIALIZER )
                ;
    }

    private static Module createHibernate5Module(SessionFactory sessionFactory) {
        return sessionFactory == null ? new Hibernate5Module() : new Hibernate5Module( sessionFactory );
    }

    public static String writeToJson(Object value) throws RuntimeException {
        return writeToJson( value, false );
    }

    public static String writeToJson(Object value, boolean pretty) throws RuntimeException {
        try {
            StringWriter writer = new StringWriter();
            JsonGenerator generator = OBJECT_MAPPER.getFactory().createGenerator( writer );
            if (pretty) {
                OBJECT_MAPPER.writerWithDefaultPrettyPrinter().writeValue( generator, value );
            } else {
                OBJECT_MAPPER.writeValue( generator, value );
            }
            return writer.toString();
        } catch (IOException e) {
            throw new RuntimeException( e );
        }
    }


}
